package image.filter;

import image.processing.ImageProcessor;
import image.utils.ImageUtilities;
import image.utils.ImageUtilitiesImpl;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;

import com.sun.media.jai.util.ImageUtil;

public abstract class Filter {

	protected int dimension;

	/**
	 * Extiende n de cada lados
	 * 
	 * @param img
	 * @param n
	 * @return
	 */
	protected BufferedImage extendImage(BufferedImage img, int n) {
		BufferedImage ans;
		int color;

		if (img == null) {
			return null;
		} else {
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(img.getWidth() + 2 * n, img.getHeight()
						+ 2 * n, BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();

				for (int x = 0; x < ans.getWidth(); x++) {
					for (int y = 0; y < ans.getHeight(); y++) {
						if (x >= 0 && x <= n) {
							ans.getRaster().setSample(x, 0, 0, 0);
						} else if (y >= 0 && y <= n) {
							ans.getRaster().setSample(0, y, 0, 0);
						} else if (x > img.getWidth() + n
								&& x <= ans.getWidth()) {
							ans.getRaster().setSample(x, y, 0, 0);
						} else if (y > img.getHeight() + n
								&& y <= ans.getHeight()) {
							ans.getRaster().setSample(x, y, 0, 0);
						} else {
							int sample = img.getRaster().getSample(x - n - 1,
									y - n - 1, 0);
							ans.getRaster().setSample(x, y, 0, sample);
						}
					}
				}

			} else {
				ans = new BufferedImage(img.getWidth() + 1,
						img.getHeight() + 1, BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster = img.getRaster();
				for (int x = 0; x < img.getWidth() + 1; x++) {
					for (int y = 0; y < img.getHeight() + 1; y++) {
						if (x >= 0 && x <= n) {
							ans.getRaster().setSample(x, 0, 0, 0);
							ans.getRaster().setSample(x, 0, 1, 0);
							ans.getRaster().setSample(x, 0, 2, 0);

						} else if (y >= 0 && y <= n) {
							ans.getRaster().setSample(0, y, 0, 0);
							ans.getRaster().setSample(0, y, 1, 0);
							ans.getRaster().setSample(0, y, 2, 0);

						} else if (x > img.getWidth() + n
								&& x <= ans.getWidth()) {
							ans.getRaster().setSample(x, y, 0, 0);
							ans.getRaster().setSample(x, y, 1, 0);
							ans.getRaster().setSample(x, y, 2, 0);
						} else if (y > img.getHeight() + n
								&& y <= ans.getHeight()) {
							ans.getRaster().setSample(x, y, 0, 0);
							ans.getRaster().setSample(x, y, 1, 0);
							ans.getRaster().setSample(x, y, 2, 0);
						} else {
							ans.getRaster().setSample(
									x,
									y,
									0,
									img.getRaster().getSample(x - n - 1,
											y - n - 1, 0));
							ans.getRaster().setSample(
									x,
									y,
									1,
									img.getRaster().getSample(x - n - 1,
											y - n - 1, 1));
							ans.getRaster().setSample(
									x,
									y,
									2,
									img.getRaster().getSample(x - n - 1,
											y - n - 1, 2));
						}
					}
				}
			}
		}
		return ans;
	}

	/*
	 * Exitendo primero la imagen luego aplico el filtro
	 */
	public BufferedImage applyFilter(BufferedImage img,
			ImageProcessor.CompressAlgorithm algorithm) {
		BufferedImage ans = null;
		int color;

		if (img == null) {
			return null;
		} else {
			if (img.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
				img = ImageUtilitiesImpl.getInstance().toGrayScale(img);
			}
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {
				ImageUtilities utilimg = ImageUtilitiesImpl.getInstance();
			//	BufferedImage extendedImage = extendImage(img, dimension / 2);
				BufferedImage extendedImage = utilimg.addPaddingEdge(img, dimension/2);
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();
				int intValues[][] = new int[img.getWidth()][img.getHeight()];
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						int value;
						int[][] mask = new int[dimension][dimension];
						for (int i = x, maskx = 0; i < x + mask.length; i++, maskx++) {
							for (int j = y, masky = 0; j < y + mask[0].length; j++, masky++) {
								mask[maskx][masky] = extendedImage.getRaster()
										.getSample(i, j, 0);
							}
						}
						value = calculateValue(mask);
						intValues[x][y] = value;
					}
				}
				ans = ImageProcessor.generateCompressedImage1Channel(intValues,
						algorithm);
			} else {
				ImageUtilities utilimg = ImageUtilitiesImpl.getInstance();
				BufferedImage extendedImage = utilimg.addPaddingEdge(img, dimension/2);
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster = img.getRaster();
				int intValues0[][] = new int[img.getWidth()][img.getHeight()];
				int intValues1[][] = new int[img.getWidth()][img.getHeight()];
				int intValues2[][] = new int[img.getWidth()][img.getHeight()];
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						int value0, value1, value2;
						int[][] mask0 = new int[dimension][dimension];
						int[][] mask1 = new int[dimension][dimension];
						int[][] mask2 = new int[dimension][dimension];

						for (int i = x, maskx = 0; i < x + mask0.length; i++, maskx++) {
							for (int j = y, masky = 0; j < y + mask0[0].length; j++, masky++) {
								mask0[maskx][masky] = extendedImage.getRaster()
										.getSample(i, j, 0);
								mask1[maskx][masky] = extendedImage.getRaster()
										.getSample(i, j, 1);
								mask2[maskx][masky] = extendedImage.getRaster()
										.getSample(i, j, 2);
							}
						}
						value0 = calculateValue(mask0);
						value1 = calculateValue(mask1);
						value2 = calculateValue(mask2);
						intValues0[x][y] = value0;
						intValues1[x][y] = value1;
						intValues2[x][y] = value2;
					}
				}
				ans = ImageProcessor.generateCompressedImage3Channel(
						intValues0, intValues1, intValues2, algorithm);
			}
			return ans;
		}

	}

	protected abstract int calculateValue(int[][] mask);
}
